#import "ColumnMatrix.h"
#import "Article.h"
#import "MatrixSet.h"
#include <assert.h>
#import "instr.h"
#import "regexpr.h"
#import "MatrixScroller.h"

@implementation ColumnMatrix

#define MOD(a,b) ((a)<0)? ((a)+(b))%(b) : (a)%(b)

- awakeFromNib
{
   buttonTitle=NXCopyStringBuffer([selectionButton title]);
   return self;
}

- free
{
   if(myVisibleCellList!=nil)
      [myVisibleCellList free];
   free(buttonTitle);
   return [super free];
}

- initFrame:(const NXRect *)frameRect
{
  NXSize newIntercell = {0.0, 0.0};
  NXSize newCellSize;

  [super initFrame:frameRect mode:NX_LISTMODE prototype:[Cell new] numRows:0 numCols:0];
  [protoCell setBordered:FALSE];
  [protoCell setAlignment:NX_LEFTALIGNED];
  
  newCellSize.height=16.0;
  newCellSize.width = frameRect->size.width;

  [self setCellSize:&newCellSize];
  [self setIntercell:&newIntercell];
  [self setBackgroundGray:NX_LTGRAY];
  [self setAutoscroll:TRUE];

  return self;
}

- superviewSizeChanged:(const NXSize*)oldSize
{
  NXSize	newCellSize;
  NXRect aRect;

  [super superviewSizeChanged:oldSize];
  
  [superview getFrame:&aRect];
  
  newCellSize.width = aRect.size.width;
  newCellSize.height = cellSize.height;
  [self setCellSize:&newCellSize];
  [self sizeToCells];
  return self;
}

- scrollUp
{
   NXSize visibleSize;
   NXRect theUpperRect;

   [[[self superview] superview] getContentSize:&visibleSize];
   NXSetRect(&theUpperRect,0.0,0.0,visibleSize.width,visibleSize.height);
   [self scrollRectToVisible:&theUpperRect];

   return self;
}
   
- loadMatrix
{
   int i,j;

   for(i=numRows-1;i>=0;i--)
      [self removeRowAt:i andFree:NO];
   [self makeVisibleCellList:self];
   j=[myVisibleCellList count];
   for(i=0;i<j;i++)
      [self addRow];
   [self sizeToCells];
   [self clearSelectedCell];

   return self;
}

- reloadMatrix
{
   List *selList;
   int lastCellPos=NX_NOT_IN_LIST;
   int i,j;

  selList=[self getSelectedCells:nil];
  [self loadMatrix];
  if(selList==nil)
     return self;
  j=[selList count];
  for(i=0;i<j;i++){
     id aCell=[selList objectAt:i];
     if([aCell isTaged]==TRUE){
        int pos=[cellList indexOf:aCell];
        if(pos!=NX_NOT_IN_LIST){
           [self setSelectionFrom:pos to:pos anchor:pos lit:YES];
           lastCellPos=pos;
        }
     }    
   }
   [selList free];
   if(lastCellPos!=NX_NOT_IN_LIST)
      [self scrollCellToVisible:lastCellPos upperOffset:1.5 lowerOffset:1.5];
  
   return self;
}

- setMatrixCellList:(List *)aList
{
   myCellList=aList;
   return self;
}

- makeVisibleCellList:sender
{
   int i,j;

   if(myVisibleCellList!=nil)
      [myVisibleCellList free];
   myVisibleCellList=[[List alloc] init];
   j=[myCellList count];
   for(i=0;i<j;i++){
      id aCell=[myCellList objectAt:i];
      if([aCell isTaged]==TRUE)
         [myVisibleCellList addObject:aCell];
   }
   return self;
}


- makeCellAt:(int)row :(int)col
{
   assert(col==0);
   return [myVisibleCellList objectAt:row];
}

- (id)selectionButton
{
   return selectionButton;
}

- removeInvalidCell:aCell
{
   return [self removeInvalidCell:aCell andUpdate:YES];
}

- removeInvalidCell:aCell andUpdate:(BOOL)update
{
   int pos=[cellList indexOf:aCell];

   [myCellList removeObject:aCell];
   [myVisibleCellList removeObject:aCell];
   if(pos==NX_NOT_IN_LIST){
      NX_ASSERT(pos==NX_NOT_IN_LIST,"Internal DS mismatch");
      return self;
   }
   [self removeRowAt:pos andFree:YES];
   if(update==YES)
      [self update];
   return self;
}

- update
{
   [self reloadMatrix];
   [self display];
   [mySet sync];
   [NXApp updateWindows];

   return self;
}

- setButtonTitle:(const char *)title
{
   free(buttonTitle);
   [selectionButton setTitle:title];
   buttonTitle=NXCopyStringBuffer(title);

   return self;
}

- _delayedUpdate:sender
{
   [selectionButton setTitle:buttonTitle];
   return self;
}

- updateButtonTitle
{
   [self perform:@selector(_delayedUpdate:) with:self afterDelay:0.0 cancelPrevious:TRUE];  
   return self;
}

- (List *)getCurrSelections
{
   return [self getSelectedCells:nil];
}

- currSelection
{
   return [self selectedCell];
}

- (BOOL)selectNextCell:(int)delta
{
    int i,j;
    id cSel;

    NX_ASSERT(((delta==1) || (delta==-1)),"Wrong parameter selectNextCell");
    
    j=[myVisibleCellList count];
    if(j==0)
       return FALSE;

    if((cSel=[mySet currentSelection])!=nil){
        i=[myVisibleCellList indexOf:cSel];
        NX_ASSERT(i!=NX_NOT_IN_LIST,"Internal DS mismatch");
        if((delta>0) && (i==j-1))
           return FALSE; //last article
        else
          if((delta<0) && (i==0))
             return FALSE; //first article
    }
    else
       if(delta>0)
          i=-1;
       else
          i=j;

   [self selectCellAt:i+delta :0];
   [self scrollCellToVisible:i+delta upperOffset:((delta==-1)? 1.5:0.0) lowerOffset:((delta==1)? 1.5:0.0)];
   [self sendAction];
 
   return TRUE;
}

- selectAll:sender
{

   [super selectAll:sender];
   [self sendAction];

   return self;
}

- mySet
{
   return mySet;
}

//-------------------------
// searchable text protocol
//-------------------------

- (oneway void)makeSelectionVisible
{
   id selCell=[self selectedCell];

   if(selCell!=nil)
      [self scrollCellToVisible:[[self cellList] indexOf:selCell] 
            upperOffset:(findNext? 0.0:1.5) lowerOffset:(findNext? 1.5:0.0)];
}
 
- (int)replaceAll:(const char *)pattern with:(const char *)replacement mode:(SearchMode)mode regexpr:(BOOL)regexpr cases:(BOOL)cases
{
   return SEARCH_CANNOT_WRITE;
}

- (oneway void)replaceSelection:(const char *)replacement
{
}

- (const char *)stringValueForCellAt:(int)index
{
   return [[self cellAt:index :0] stringValue];
}

- (int)searchFor:(const char *)pattern mode:(SearchMode)mode reverse:(BOOL)rev regexpr:(BOOL)regexpr cases:(BOOL)cases position:(out int *)pos size:(out int *)size
{
   int rows,cols,i;
   int s_pos;
   int fStart,fEnd;
   int delta;

   unsigned char fm[256], tr[256];
   struct re_pattern_buffer rpat;

   findNext=!rev;

   s_pos=-2;
   fStart=fEnd=-2;

   [self getNumRows:&rows numCols:&cols];
   if(rows==0)
      return 0;

   // find first selected cell
   for(i=0;i<rows;i++)
      if([[self cellAt:i :0] state]){
         s_pos=i;
         break;
      }

   if(!rev){
      if(s_pos<0){ 
         fStart=0; fEnd=rows; 
      }
      else{
         fStart=s_pos+1; fEnd=s_pos+rows; 
      }
   }
   else{
      if(s_pos<0){ 
         fStart=rows-1; fEnd=-1; 
      }
      else{
         fStart=s_pos-1+rows; fEnd=s_pos; 
      }
   }
   
   delta= rev? -1:1;
  
   if(regexpr){
      char *str;
      int i;

      memset(&rpat, 0, sizeof(rpat));
      for(i=256; i--;)
         tr[i] = i;
      if(!cases)
         for(i='A'; i<='Z'; i++)
            tr[i] = i-'A'+'a';
      rpat.translate = tr;
      rpat.fastmap = fm;
      str = re_compile_pattern((char *)pattern,strlen(pattern), &rpat);
      if (str!=NULL)
        return (strcmp(str, "Out of memory")?SEARCH_INVALID_REGEXPR:SEARCH_INTERNAL_ERROR);
   }

   for(;fStart!=fEnd;fStart+=delta){
      int index=fStart%rows;
      const char *result=NULL;
      const char *cellString=[self stringValueForCellAt:index];

      if(regexpr){
        int l=strlen(cellString);
        int p=re_search_pattern(&rpat,(char *)cellString,l,0,l,0);

        if(p==-2)
           return SEARCH_INTERNAL_ERROR;
        result= (p==-1)? NULL : cellString;
      }
      else
         result=instr(cellString,pattern,cases);

      if(result!=NULL){
         *pos=index;
         *size=1;
         return 1;
      }
   }
   
   return 0;
}

- (oneway void)selectTextFrom:(int)start to:(int)end
{
   int rows,cols;
   if(start<=end){
      if(start==end){
         [self selectCellAt:-1 :-1];
         [self sendAction];
      }
      else{
         [self getNumRows:&rows numCols:&cols];
         if(end<=rows){
            [self selectCellAt:start :0];
            [self sendAction];
         }
      }
   }
}
      
- (void)writeSelectionToPasteboard:(in Pasteboard *)pboard asType:(in NXAtom)type
{
	id aList =[self getCurrSelections];
	
   
   if([aList count]==1){
      const char *sval=[[aList objectAt:0] stringValue];
      [pboard declareTypes:&type num:1 owner:NULL];
      [pboard writeType:type data:sval length:strlen(sval)];
   }
	[aList free];
	
   return;
}

@end
